package cn.core.sign.service;

import cn.core.sign.ao.LoginAO;
import cn.core.sign.bo.ConfBOAO;
import cn.core.sign.eo.*;
import cn.core.sign.mapper.*;
import cn.core.sign.qo.*;
import cn.core.sign.vo.LoginVO;
import cn.core.sign.vo.PostLoginInfoVO;
import cn.core.sign.vo.StaffPostListVO;
import cn.core.sign.vo.SwitchPostLoginInfoVO;
import cn.core.tool.context.CurrentContextData;
import cn.core.tool.event.CoreDataEvent;
import cn.core.tool.exception.CoreException;
import cn.core.tool.util.CoreTool;
import com.google.gson.Gson;
import com.google.gson.reflect.TypeToken;
import lombok.extern.log4j.Log4j2;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.Collection;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;

/**
 * 登录 服务
 */
@Log4j2
@Service
public class SignService {

  @Value("${proconf.token-expire-browser}")
  private Long tokenExpireBrowser;

  @Resource
  private Gson gson;

  @Resource
  private AccountMapper accountMapper;

  @Resource
  private UsrMapper usrMapper;

  @Resource
  private SessionsMapper sessionsMapper;

  @Resource
  private LoginLogMapper loginLogMapper;

  @Resource
  private SignMapper signMapper;

  @Value("${spring.profiles.active}")
  private String confEnv;

  @Resource
  private ApplicationEventPublisher applicationEventPublisher;

  @Resource
  private StaffMapper staffMapper;

  @Resource
  private OrgMapper orgMapper;

  /**
   * 登录
   */
  @Transactional(propagation = Propagation.REQUIRED, readOnly = false, rollbackFor = Exception.class)
  public LoginVO login(LoginAO param) throws Exception {
    try {
      if (CoreTool.isNullEo(param)) {
        throw new CoreException(CoreException.E_PARAM_ERR);
      }
      LoginVO loginVO = new LoginVO().setSwitchPostLoginInfo(new SwitchPostLoginInfoVO().setPostList(new ArrayList<>()));
      if ("Browser".equals(param.getLoginSide())) {
        // 浏览器端登录
        String token = CoreTool.getUUID32();
        Long times = System.currentTimeMillis();
        // 返回给前端的用户id可能是 账号id 用户id 岗位id
        String usrId = null;
        // 返回给前端的用户名可能是 账号 用户名 员工姓名
        String usrname = null;
        // 获取默认头像url配置信息
        CoreDataEvent coreDataEventAvatarUrl = new CoreDataEvent("getConfInfo", new ConfBOAO().setConfType("getValueByKey").setConfEnv(confEnv).setConfKey("defaultAvatarUrl"));
        applicationEventPublisher.publishEvent(coreDataEventAvatarUrl);
        String avatarUrl = CoreTool.isNotNullStr(coreDataEventAvatarUrl.getData()) ? coreDataEventAvatarUrl.getData() : "";
        LinkedHashSet<String> authList = new LinkedHashSet<String>();
        authList.add("coreHome");
        if (CoreTool.isNotNullStr(param.getAccount()) && CoreTool.isNotNullStr(param.getPassword())) {
          // 账号密码登录
          Collection<Account> accountSet = accountMapper.query(
              new AccountQO()
                  .setAccount(param.getAccount()));
          if (CoreTool.isNotEmpty(accountSet)) {
            // 账号存在
            Account account = accountSet.iterator().next();
            String password = CoreTool.encodeAES(CoreTool.decodeAES(param.getPassword(), CoreTool.AES_KEY), CoreTool.AES_KEY_PASSWORD);
            if (account.getPassword().equals(password)) {
              // 密码正确
              // 设置登录日志
              LoginLog loginLog = new LoginLog()
                  .setId(CoreTool.getUUID32())
                  .setLoginSide(param.getLoginSide())
                  .setLoginTimes(times)
                  .setAccountId(account.getId())
                  .setAccount(account.getAccount());
              // 设置会话信息
              Sessions sessions = new Sessions()
                  .setId(token)
                  .setLoginSide(param.getLoginSide())
                  .setCurrentTimes(times)
                  .setAccountId(account.getId());
              // 设置线程缓存信息
              CurrentContextData currentContextData = new CurrentContextData()
                  .setAesKey(CoreTool.getAESKey(token))
                  .setLoginSide(param.getLoginSide())
                  .setAccountId(account.getId())
                  .setAccount(account.getAccount());
              if (account.getTokenExpireBrowser() != null) {
                // 账号内设置了浏览器端令牌过期时间
                if (account.getTokenExpireBrowser() == 0l) {
                  // 令牌永不过期 默认设置为365天
                  currentContextData.setTokenExpireBrowser(31536000000l);
                } else {
                  currentContextData.setTokenExpireBrowser(account.getTokenExpireBrowser());
                }
              } else {
                // 账号没有设置浏览器端令牌过期时间
                // 使用默认令牌过期时间
                if (tokenExpireBrowser == 0l) {
                  // 令牌永不过期 默认设置为365天
                  currentContextData.setTokenExpireBrowser(31536000000l);
                } else {
                  currentContextData.setTokenExpireBrowser(tokenExpireBrowser);
                }
              }
              if (CoreTool.isNotNullStr(account.getNexusType())) {
                // 账号已关联用户或员工
                loginLog.setNexusType(account.getNexusType());
                currentContextData.setNexusType(account.getNexusType());
                if ("usr".equals(account.getNexusType())) {
                  // 关联用户
                  if (CoreTool.isNotNullStr(account.getNexusObjectId())) {
                    // 关联的用户ID不为空
                    loginLog.setNexusObjectId(account.getNexusObjectId());
                    currentContextData.setNexusObjectId(account.getNexusObjectId());
                    Usr usr = usrMapper.pk(
                        new UsrQO()
                            .setId(account.getNexusObjectId()));
                    if (CoreTool.isNullEo(usr)) {
                      // 关联的用户ID不存在
                      usrId = account.getId();
                      usrname = account.getAccount();
                      loginVO.setAuthList(authList);
                    } else {
                      // 关联的用户ID存在
                      loginLog.setMphoneNo(usr.getMphoneNo());
                      loginLog.setEmail(usr.getEmail());
                      currentContextData.setUsrId(usr.getId());
                      currentContextData.setUsrType(usr.getUsrType());
                      currentContextData.setMobilePhoneNumber(usr.getMphoneNo());
                      usrId = usr.getId();
                      usrname = usr.getUsrname();
                      if (CoreTool.isNotNullStr(usr.getAvatarUrl())) {
                        avatarUrl = usr.getAvatarUrl();
                      }
                      LinkedHashSet<String> cAuthListSet = new LinkedHashSet<String>(signMapper.getAuthListByUsrIdLoginSide(usr.getId(), param.getLoginSide()));
                      if (CoreTool.isNotEmpty(cAuthListSet)) {
                        // 授权列表不为空
                        loginVO.setAuthList(cAuthListSet);
                      } else {
                        // 授权列表为空
                        loginVO.setAuthList(authList);
                      }
                    }
                  } else {
                    // 关联的用户ID为空
                    loginVO.setAuthList(authList);
                  }
                } else if ("staff".equals(account.getNexusType())) {
                  // 关联员工
                  if (CoreTool.isNotNullStr(account.getNexusObjectId())) {
                    // 关联的员工ID不为空
                    loginLog.setNexusObjectId(account.getNexusObjectId());
                    currentContextData.setNexusObjectId(account.getNexusObjectId());
                    Staff staff = staffMapper.pk(
                        new StaffQO()
                            .setId(account.getNexusObjectId()));
                    if (CoreTool.isNullEo(staff)) {
                      // 关联的员工ID不存在
                      usrId = account.getId();
                      usrname = account.getAccount();
                      loginVO.setAuthList(authList);
                    } else {
                      // 关联的员工ID存在
                      loginLog.setMphoneNo(staff.getMphoneNo());
                      loginLog.setEmail(staff.getEmail());
                      currentContextData.setUsrId(staff.getId());
                      currentContextData.setMobilePhoneNumber(staff.getMphoneNo());
                      usrId = staff.getId();
                      usrname = staff.getStaffName();
                      if (CoreTool.isNotNullStr(staff.getAvatarUrl())) {
                        avatarUrl = staff.getAvatarUrl();
                      }
                      LinkedHashSet<String> cAuthListSet = new LinkedHashSet<String>(CoreTool.isNotNullStr(param.getPostId()) ? signMapper.getAuthListByPostIdLoginSide(param.getPostId(), param.getLoginSide()) : signMapper.getAuthListByStaffIdLoginSide(staff.getId(), param.getLoginSide()));
                      if (CoreTool.isNotEmpty(cAuthListSet)) {
                        // 授权列表不为空
                        loginVO.setAuthList(cAuthListSet);
                      } else {
                        // 授权列表为空
                        loginVO.setAuthList(authList);
                      }
                      // 切换岗位登录信息
                      SwitchPostLoginInfoVO switchPostLoginInfoVO = new SwitchPostLoginInfoVO();
                      LinkedHashSet<PostLoginInfoVO> postLoginInfoVOSet = new LinkedHashSet<>();
                      LinkedHashMap<String, String> orgNameAbbrMapInfo = new LinkedHashMap<>();
                      Collection<Org> rOrgSet = orgMapper.query(new OrgQO().setWeightOrderAsc("y"));
                      if (CoreTool.isNotEmpty(rOrgSet)) {
                        for (Org eo : rOrgSet) {
                          orgNameAbbrMapInfo.put(eo.getId(), eo.getOrgNameAbbr());
                        }
                      }
                      Collection<StaffPostListVO> rStaffPostListVOSet = signMapper.getStaffPostListByStaffId(staff.getId());
                      if (CoreTool.isNotEmpty(rStaffPostListVOSet)) {
                        switchPostLoginInfoVO.setShow(true);
                        String currPostName = null;
                        for (StaffPostListVO eo : rStaffPostListVOSet) {
                          String postName = CoreTool.renderString("${" + eo.getIdPath().replaceAll("/", "}/\\${") + "}", orgNameAbbrMapInfo) + " - [" + eo.getPostName() + "]";
                          if (CoreTool.isNotNullStr(param.getPostId())) {
                            if (param.getPostId().equals(eo.getPostId())) {
                              currPostName = postName;
                            }
                          } else {
                            if ("y".equals(eo.getDefaultPost())) {
                              currPostName = postName;
                            }
                          }
                          postLoginInfoVOSet.add(new PostLoginInfoVO()
                              .setLoginSide(param.getLoginSide())
                              .setAccount(param.getAccount())
                              .setPassword(param.getPassword())
                              .setPostId(eo.getPostId())
                              .setPostName(postName)
                              .setDisabled(CoreTool.isNotNullStr(param.getPostId()) ? param.getPostId().equals(eo.getPostId()) ? true : false : "y".equals(eo.getDefaultPost()) ? true : false));
                        }
                        if (CoreTool.isNotNullStr(currPostName)) {
                          switchPostLoginInfoVO.setCurrPost(currPostName);
                        }
                      }
                      if (CoreTool.isNotEmpty(postLoginInfoVOSet)) {
                        switchPostLoginInfoVO.setPostList(postLoginInfoVOSet);
                      }
                      loginVO.setSwitchPostLoginInfo(switchPostLoginInfoVO);
                    }
                  } else {
                    // 关联的员工ID为空
                    loginVO.setAuthList(authList);
                  }
                } else {
                  // 未知关联类型
                  usrId = account.getId();
                  usrname = account.getAccount();
                  loginVO.setAuthList(authList);
                }
              } else {
                // 账号未关联用户或员工
                usrId = account.getId();
                usrname = account.getAccount();
                loginVO.setAuthList(authList);
              }
              loginLog.setUsrname(usrname);
              currentContextData.setUsrname(usrname);
              sessions.setSessionsInfo(gson.toJson(currentContextData));
              // 设置登录返回信息
              loginVO.setMsg(CoreTool.isNullStr(param.getPostId()) ? "登录成功！" : "切换岗位登录成功，将自动刷新页面！");
              loginVO.setAccountId(account.getId());
              loginVO.setUsrId(usrId);
              loginVO.setUsrname(usrname);
              loginVO.setAvatarUrl(avatarUrl);
              loginVO.setToken(token);
              loginVO.setAesKey(CoreTool.encodeRSAByPublicKey(currentContextData.getAesKey(), CoreTool.PUBLIC_KEY));
              // 数据字典 >>> 获取所有数据字典和数据字典值键值信息
              CoreDataEvent coreDataEventDict = new CoreDataEvent("getAllDictValueMapInfo");
              applicationEventPublisher.publishEvent(coreDataEventDict);
              loginVO.setAllDictValueMapInfo(CoreTool.isNotNullStr(coreDataEventDict.getData()) ? coreDataEventDict.getData(new TypeToken<LinkedHashMap<String, LinkedHashMap<String, String>>>(){}) : new LinkedHashMap<String, LinkedHashMap<String, String>>());
              // 系统配置 >>> 获取所有配置键值信息
              CoreDataEvent coreDataEventConf = new CoreDataEvent("getAllConfMapInfo", new ConfBOAO().setConfType("getValueByKey").setConfEnv(confEnv));
              applicationEventPublisher.publishEvent(coreDataEventConf);
              loginVO.setAllConfMapInfo(CoreTool.isNotNullStr(coreDataEventConf.getData()) ? coreDataEventConf.getData(new TypeToken<LinkedHashMap<String, String>>(){}) : new LinkedHashMap<String, String>());
              // 保存登录日志
              loginLogMapper.save(loginLog);
              Collection<Sessions> sessionsSet = sessionsMapper.query(
                  new SessionsQO()
                      .setLoginSide(param.getLoginSide())
                      .setAccountId(account.getId()));
              if (CoreTool.isNotEmpty(sessionsSet)) {
                // 已登录
                // 修改会话信息
                sessionsMapper.updateQuery(sessions, new SessionsQO().setId(sessionsSet.iterator().next().getId()));
              } else {
                // 未登录
                // 保存会话信息
                sessionsMapper.save(sessions);
              }
            } else {
              // 密码错误
              throw new CoreException("账号或密码错误！");
            }
          } else {
            // 账号不存在
            throw new CoreException("账号或密码错误！");
          }
        } else {
          // 账号密码为空转到手机号登录
          if (CoreTool.isNotNullStr(param.getMphoneNo()) && CoreTool.isNotNullStr(param.getVerificationCode())) {
            // 手机号登录
          } else {
            // 登录信息为空
            throw new CoreException("登录失败！");
          }
        }
      } else if ("Android".equals(param.getLoginSide())) {
        // 安卓app登录
      } else if ("ios".equals(param.getLoginSide())) {
        // 苹果app登录
      } else if ("Wechatapp".equals(param.getLoginSide())) {
        // 微信小程序登录
      } else {
        // 未知登录标识
        throw new CoreException("登录失败！");
      }
      return loginVO;
    } catch (Exception e) {
      if (log.isDebugEnabled()) {
        log.error(e.getMessage(), e);
      }
      throw e;
    }
  }

}
